From a1980f22aedd9add1f5d409121cafcd165520f2c Mon Sep 17 00:00:00 2001 From: "kaf24@scramble.cl.cam.ac.uk" Date: Tue, 4 Nov 2003 12:48:18 +0000 Subject: [PATCH] bitkeeper revision 1.554 (3fa7a01284xyjU8eM36NSWyFCKOIsQ) Many files: More support in the xenolinux guest os for suspending itself. --- xen/drivers/block/xen_block.c | 20 ++- xen/include/hypervisor-ifs/network.h | 2 +- xen/include/hypervisor-ifs/vbd.h | 17 +-- xen/net/dev.c | 18 +++ .../arch/xeno/drivers/block/xl_block.c | 70 ++++++++-- .../arch/xeno/drivers/network/network.c | 127 +++++++++++------- .../arch/xeno/kernel/setup.c | 63 ++++++++- xenolinux-2.4.22-sparse/arch/xeno/mm/init.c | 20 +-- .../include/asm-xeno/fixmap.h | 3 + 9 files changed, 257 insertions(+), 83 deletions(-) diff --git a/xen/drivers/block/xen_block.c b/xen/drivers/block/xen_block.c index e3daf43ae4..c96c7f5326 100644 --- a/xen/drivers/block/xen_block.c +++ b/xen/drivers/block/xen_block.c @@ -246,6 +246,7 @@ long do_block_io_op(block_io_op_t *u_block_io_op) { long ret = 0; block_io_op_t op; + struct task_struct *p = current; if (copy_from_user(&op, u_block_io_op, sizeof(op))) return -EFAULT; @@ -254,17 +255,32 @@ long do_block_io_op(block_io_op_t *u_block_io_op) case BLOCK_IO_OP_SIGNAL: /* simply indicates there're reqs outstanding => add current to list */ - add_to_blkdev_list_tail(current); + add_to_blkdev_list_tail(p); maybe_trigger_io_schedule(); break; case BLOCK_IO_OP_ATTACH_VBD: /* attach a VBD to a given domain; caller must be privileged */ - if(!IS_PRIV(current)) + if( !IS_PRIV(p) ) return -EPERM; ret = vbd_attach(&op.u.attach_info); break; + case BLOCK_IO_OP_RESET: + /* Avoid a race with the tasklet. */ + remove_from_blkdev_list(p); + if ( p->blk_req_cons != p->blk_resp_prod ) + { + /* Interface isn't quiescent. */ + ret = -EINVAL; + } + else + { + p->blk_req_cons = p->blk_resp_prod = 0; + ret = 0; + } + break; + default: ret = -ENOSYS; } diff --git a/xen/include/hypervisor-ifs/network.h b/xen/include/hypervisor-ifs/network.h index 8681b5253e..d328fa0118 100644 --- a/xen/include/hypervisor-ifs/network.h +++ b/xen/include/hypervisor-ifs/network.h @@ -18,7 +18,7 @@ #define NETOP_PUSH_BUFFERS 0 /* Notify Xen of new buffers on the rings. */ #define NETOP_FLUSH_BUFFERS 1 /* Flush all pending request buffers. */ - +#define NETOP_RESET_RINGS 2 /* Reset ring indexes on a quiescent vif. */ typedef struct tx_req_entry_st { diff --git a/xen/include/hypervisor-ifs/vbd.h b/xen/include/hypervisor-ifs/vbd.h index c075bb06c0..8cbd059d0b 100644 --- a/xen/include/hypervisor-ifs/vbd.h +++ b/xen/include/hypervisor-ifs/vbd.h @@ -78,9 +78,9 @@ typedef struct xen_vbd_info /* Block I/O trap operations and associated structures. */ -#define BLOCK_IO_OP_SIGNAL 0 // let xen know we have work to do -#define BLOCK_IO_OP_ATTACH_VBD 1 // attach a VBD to a given domain - +#define BLOCK_IO_OP_SIGNAL 0 /* let xen know we have work to do */ +#define BLOCK_IO_OP_ATTACH_VBD 1 /* attach a VBD to a given domain */ +#define BLOCK_IO_OP_RESET 2 /* reset ring indexes on quiescent i/f */ typedef struct _extent { u16 raw_device; @@ -91,10 +91,10 @@ typedef struct _extent { typedef struct _vbd_attach { int domain; - u16 mode; // read-only or read-write - u16 device; // how this domain refers to this VBD - int nr_extents; // number of extents in the VBD - extent_t *extents; // pointer to /array/ of extents + u16 mode; /* read-only or read-write */ + u16 device; /* how this domain refers to this VBD */ + int nr_extents; /* number of extents in the VBD */ + extent_t *extents; /* pointer to /array/ of extents */ } vbd_attach_t; @@ -103,8 +103,9 @@ typedef struct block_io_op_st unsigned long cmd; union { - long signal_val_unused; + /* no entry for BLOCK_IO_OP_SIGNAL */ vbd_attach_t attach_info; + /* no entry for BLOCK_IO_OP_RESET */ } u; } block_io_op_t; diff --git a/xen/net/dev.c b/xen/net/dev.c index b2a4212e3e..2250915222 100644 --- a/xen/net/dev.c +++ b/xen/net/dev.c @@ -2229,6 +2229,24 @@ long do_net_io_op(unsigned int op, unsigned int idx) ret = flush_bufs_for_vif(vif); break; + case NETOP_RESET_RINGS: + /* We take the tx_lock to avoid a race with get_tx_bufs. */ + spin_lock_irq(&vif->tx_lock); + if ( (vif->rx_req_cons != vif->rx_resp_prod) || + (vif->tx_req_cons != vif->tx_resp_prod) ) + { + /* The interface isn't quiescent. */ + ret = -EINVAL; + } + else + { + vif->rx_req_cons = vif->rx_resp_prod = 0; + vif->tx_req_cons = vif->tx_resp_prod = 0; + ret = 0; + } + spin_unlock_irq(&vif->tx_lock); + break; + default: ret = -EINVAL; break; diff --git a/xenolinux-2.4.22-sparse/arch/xeno/drivers/block/xl_block.c b/xenolinux-2.4.22-sparse/arch/xeno/drivers/block/xl_block.c index 2d08689b39..c3cf866de5 100644 --- a/xenolinux-2.4.22-sparse/arch/xeno/drivers/block/xl_block.c +++ b/xenolinux-2.4.22-sparse/arch/xeno/drivers/block/xl_block.c @@ -14,13 +14,20 @@ typedef unsigned char byte; /* from linux/ide.h */ #define XLBLK_RESPONSE_IRQ _EVENT_BLKDEV #define DEBUG_IRQ _EVENT_DEBUG +#define STATE_ACTIVE 0 +#define STATE_SUSPENDED 1 +#define STATE_CLOSED 2 +static unsigned int state = STATE_SUSPENDED; + static blk_ring_t *blk_ring; static unsigned int resp_cons; /* Response consumer for comms ring. */ static unsigned int req_prod; /* Private request producer. */ static xen_disk_info_t xlblk_disk_info; static int xlblk_control_msg_pending; -#define RING_FULL (BLK_RING_INC(req_prod) == resp_cons) +/* We plug the I/O ring if the driver is suspended or if the ring is full. */ +#define RING_PLUGGED ((BLK_RING_INC(req_prod) == resp_cons) || \ + (state != STATE_ACTIVE)) /* * Request queues with outstanding work, but ring is currently full. @@ -338,6 +345,9 @@ static int hypervisor_request(unsigned long id, if ( nr_sectors >= (1<<9) ) BUG(); if ( (buffer_ma & ((1<<9)-1)) != 0 ) BUG(); + if ( state == STATE_CLOSED ) + return 1; + switch ( operation ) { case XEN_BLOCK_VBD_CREATE: @@ -345,7 +355,7 @@ static int hypervisor_request(unsigned long id, case XEN_BLOCK_PHYSDEV_GRANT: case XEN_BLOCK_PHYSDEV_PROBE: case XEN_BLOCK_PROBE: - if ( RING_FULL ) return 1; + if ( RING_PLUGGED ) return 1; phys_device = (kdev_t) 0; sector_number = 0; DISABLE_SCATTERGATHER(); @@ -372,7 +382,7 @@ static int hypervisor_request(unsigned long id, DISABLE_SCATTERGATHER(); return 0; } - else if ( RING_FULL ) + else if ( RING_PLUGGED ) { return 1; } @@ -485,6 +495,9 @@ static void xlblk_response_int(int irq, void *dev_id, struct pt_regs *ptregs) int i; unsigned long flags; struct buffer_head *bh, *next_bh; + + if ( state == STATE_CLOSED ) + return; spin_lock_irqsave(&io_request_lock, flags); @@ -534,7 +547,7 @@ static void xlblk_response_int(int irq, void *dev_id, struct pt_regs *ptregs) while ( nr_pending != 0 ) { do_xlblk_request(pending_queues[--nr_pending]); - if ( RING_FULL ) break; + if ( RING_PLUGGED ) break; } } @@ -569,17 +582,32 @@ int xenolinux_control_msg(int operation, char *buffer, int size) } -int __init xlblk_init(void) +static void reset_xlblk_interface(void) { - int error; + block_io_op_t op; xlblk_control_msg_pending = 0; nr_pending = 0; - /* This mapping was created early at boot time. */ + op.cmd = BLOCK_IO_OP_RESET; + if ( HYPERVISOR_block_io_op(&op) != 0 ) + printk(KERN_ALERT "Possible blkdev trouble: couldn't reset ring\n"); + + set_fixmap(FIX_BLKRING_BASE, start_info.blk_ring); blk_ring = (blk_ring_t *)fix_to_virt(FIX_BLKRING_BASE); blk_ring->req_prod = blk_ring->resp_prod = resp_cons = req_prod = 0; - + + wmb(); + state = STATE_ACTIVE; +} + + +int __init xlblk_init(void) +{ + int error; + + reset_xlblk_interface(); + error = request_irq(XLBLK_RESPONSE_IRQ, xlblk_response_int, SA_SAMPLE_RANDOM, "blkdev", NULL); if ( error ) @@ -639,3 +667,29 @@ static void __exit xlblk_cleanup(void) module_init(xlblk_init); module_exit(xlblk_cleanup); #endif + + +void blkdev_suspend(void) +{ + state = STATE_SUSPENDED; + wmb(); + + while ( resp_cons != blk_ring->req_prod ) + { + barrier(); + current->state = TASK_INTERRUPTIBLE; + schedule_timeout(1); + } + + wmb(); + state = STATE_CLOSED; + wmb(); + + clear_fixmap(FIX_BLKRING_BASE); +} + + +void blkdev_resume(void) +{ + reset_xlblk_interface(); +} diff --git a/xenolinux-2.4.22-sparse/arch/xeno/drivers/network/network.c b/xenolinux-2.4.22-sparse/arch/xeno/drivers/network/network.c index 25cd221a77..00af8be834 100644 --- a/xenolinux-2.4.22-sparse/arch/xeno/drivers/network/network.c +++ b/xenolinux-2.4.22-sparse/arch/xeno/drivers/network/network.c @@ -44,12 +44,6 @@ static void cleanup_module(void); static struct list_head dev_list; -/* - * Needed because network_close() is not properly implemented yet. So - * an open after a close needs to do much less than the initial open. - */ -static int opened_once_already = 0; - struct net_private { struct list_head list; @@ -58,6 +52,7 @@ struct net_private struct net_device_stats stats; atomic_t tx_entries; unsigned int rx_resp_cons, tx_resp_cons, tx_full; + unsigned int net_ring_fixmap_idx; net_ring_t *net_ring; net_idx_t *net_idx; spinlock_t tx_lock; @@ -65,6 +60,11 @@ struct net_private unsigned int rx_bufs_to_notify; +#define STATE_ACTIVE 0 +#define STATE_SUSPENDED 1 +#define STATE_CLOSED 2 + unsigned int state; + /* * {tx,rx}_skbs store outstanding skbuffs. The first entry in each * array is an index into a chain of free entries. @@ -103,14 +103,16 @@ static void dbg_network_int(int irq, void *dev_id, struct pt_regs *ptregs) static int network_open(struct net_device *dev) { struct net_private *np = dev->priv; - int i, error = 0; + int i; - if ( opened_once_already ) - { - memset(&np->stats, 0, sizeof(np->stats)); - netif_start_queue(dev); - return 0; - } + if ( HYPERVISOR_net_io_op(NETOP_RESET_RINGS, np->idx) != 0 ) + printk(KERN_ALERT "Possible net trouble: couldn't reset ring idxs\n"); + + set_fixmap(FIX_NETRING0_BASE + np->net_ring_fixmap_idx, + start_info.net_rings[np->idx]); + np->net_ring = (net_ring_t *)fix_to_virt( + FIX_NETRING0_BASE + np->net_ring_fixmap_idx); + np->net_idx = &HYPERVISOR_shared_info->net_idx[np->idx]; np->rx_bufs_to_notify = 0; np->rx_resp_cons = np->tx_resp_cons = np->tx_full = 0; @@ -126,38 +128,16 @@ static int network_open(struct net_device *dev) for ( i = 0; i < RX_RING_SIZE; i++ ) np->rx_skbs[i] = (void *)(i+1); - error = request_irq(NET_IRQ, network_interrupt, - SA_SAMPLE_RANDOM, "network", dev); - if ( error ) - { - printk(KERN_WARNING "%s: Could not allocate network interrupt\n", - dev->name); - goto fail; - } - - error = request_irq(_EVENT_DEBUG, dbg_network_int, SA_SHIRQ, - "debug", dev); - if ( error ) - { - printk(KERN_WARNING "%s: Non-fatal error -- no debug interrupt\n", - dev->name); - } + wmb(); + np->state = STATE_ACTIVE; network_alloc_rx_buffers(dev); - printk("XenoLinux Virtual Network Driver installed as %s\n", dev->name); - netif_start_queue(dev); MOD_INC_USE_COUNT; - opened_once_already = 1; - return 0; - - fail: - kfree(np); - return error; } @@ -192,7 +172,8 @@ static void network_tx_buf_gc(struct net_device *dev) if ( np->tx_full && (atomic_read(&np->tx_entries) < TX_MAX_ENTRIES) ) { np->tx_full = 0; - netif_wake_queue(dev); + if ( np->state == STATE_ACTIVE ) + netif_wake_queue(dev); } } @@ -214,7 +195,8 @@ static void network_alloc_rx_buffers(struct net_device *dev) struct sk_buff *skb; unsigned int end = RX_RING_ADD(np->rx_resp_cons, RX_MAX_ENTRIES); - if ( (i = np->net_idx->rx_req_prod) == end ) + if ( ((i = np->net_idx->rx_req_prod) == end) || + (np->state != STATE_ACTIVE) ) return; do { @@ -312,14 +294,16 @@ static int network_start_xmit(struct sk_buff *skb, struct net_device *dev) } -static void network_interrupt(int irq, void *dev_id, struct pt_regs *ptregs) +static inline void _network_interrupt(struct net_device *dev) { + struct net_private *np = dev->priv; unsigned int i; unsigned long flags; - struct net_device *dev = (struct net_device *)dev_id; - struct net_private *np = dev->priv; struct sk_buff *skb; rx_resp_entry_t *rx; + + if ( np->state == STATE_CLOSED ) + return; spin_lock_irqsave(&np->tx_lock, flags); network_tx_buf_gc(dev); @@ -375,9 +359,46 @@ static void network_interrupt(int irq, void *dev_id, struct pt_regs *ptregs) } +static void network_interrupt(int irq, void *unused, struct pt_regs *ptregs) +{ + struct list_head *ent; + struct net_private *np; + list_for_each ( ent, &dev_list ) + { + np = list_entry(ent, struct net_private, list); + _network_interrupt(np->dev); + } +} + + int network_close(struct net_device *dev) { - netif_stop_queue(dev); + struct net_private *np = dev->priv; + + np->state = STATE_SUSPENDED; + wmb(); + + netif_stop_queue(np->dev); + + HYPERVISOR_net_io_op(NETOP_FLUSH_BUFFERS, np->idx); + + while ( (np->rx_resp_cons != np->net_idx->rx_req_prod) || + (np->tx_resp_cons != np->net_idx->tx_req_prod) ) + { + barrier(); + current->state = TASK_INTERRUPTIBLE; + schedule_timeout(1); + } + + wmb(); + np->state = STATE_CLOSED; + wmb(); + + /* Now no longer safe to take interrupts for this device. */ + clear_fixmap(FIX_NETRING0_BASE + np->net_ring_fixmap_idx); + + MOD_DEC_USE_COUNT; + return 0; } @@ -471,6 +492,18 @@ int __init init_module(void) if ( start_info.dom_id == 0 ) (void)register_inetaddr_notifier(¬ifier_inetdev); + err = request_irq(NET_IRQ, network_interrupt, + SA_SAMPLE_RANDOM, "network", NULL); + if ( err ) + { + printk(KERN_WARNING "Could not allocate network interrupt\n"); + goto fail; + } + + err = request_irq(_EVENT_DEBUG, dbg_network_int, SA_SHIRQ, "debug", NULL); + if ( err ) + printk(KERN_WARNING "Non-fatal error -- no debug interrupt\n"); + for ( i = 0; i < MAX_DOMAIN_VIFS; i++ ) { if ( start_info.net_rings[i] == 0 ) @@ -487,12 +520,10 @@ int __init init_module(void) goto fail; } - set_fixmap(FIX_NETRING0_BASE+fixmap_idx, start_info.net_rings[i]); - np = dev->priv; - np->net_ring = (net_ring_t *)fix_to_virt(FIX_NETRING0_BASE+fixmap_idx); - np->net_idx = &HYPERVISOR_shared_info->net_idx[i]; - np->idx = i; + np->state = STATE_CLOSED; + np->net_ring_fixmap_idx = fixmap_idx; + np->idx = i; SET_MODULE_OWNER(dev); dev->open = network_open; diff --git a/xenolinux-2.4.22-sparse/arch/xeno/kernel/setup.c b/xenolinux-2.4.22-sparse/arch/xeno/kernel/setup.c index 480a3afd07..6fdbf9d165 100644 --- a/xenolinux-2.4.22-sparse/arch/xeno/kernel/setup.c +++ b/xenolinux-2.4.22-sparse/arch/xeno/kernel/setup.c @@ -45,6 +45,8 @@ #include #include #include +#include +#include /* * Point at the empty zero page to start with. We map the real shared_info @@ -1035,33 +1037,82 @@ void __init cpu_init (void) * Time-to-die callback handling. */ -static void time_to_die(int irq, void *unused, struct pt_regs *regs) +static void die_irq(int irq, void *unused, struct pt_regs *regs) { extern void ctrl_alt_del(void); ctrl_alt_del(); } -static int __init setup_death_event(void) +static int __init setup_die_event(void) { - (void)request_irq(_EVENT_DIE, time_to_die, 0, "die", NULL); + (void)request_irq(_EVENT_DIE, die_irq, 0, "die", NULL); return 0; } -__initcall(setup_death_event); +__initcall(setup_die_event); /****************************************************************************** * Stop/pickle callback handling. */ -static void time_to_stop(int irq, void *unused, struct pt_regs *regs) +static void stop_task(void *unused) { + /* Hmmm... a cleaner interface to suspend/resume blkdevs would be nice. */ + extern void blkdev_suspend(void); + extern void blkdev_resume(void); + + struct net_device *dev; + char name[6]; + int i; + + /* Close down all Ethernet interfaces. */ + for ( i = 0; i < 10; i++ ) + { + sprintf(name, "eth%d", i); + if ( (dev = dev_get_by_name(name)) == NULL ) + continue; + dev_close(dev); + dev_put(dev); + } + + blkdev_suspend(); + + __cli(); + + clear_fixmap(FIX_SHARED_INFO); + HYPERVISOR_stop(); + + set_fixmap(FIX_SHARED_INFO, start_info.shared_info); + HYPERVISOR_shared_info = (shared_info_t *)fix_to_virt(FIX_SHARED_INFO); + + __sti(); + + blkdev_resume(); + + /* Bring up all Ethernet interfaces. */ + for ( i = 0; i < 10; i++ ) + { + sprintf(name, "eth%d", i); + if ( (dev = dev_get_by_name(name)) == NULL ) + continue; + dev_open(dev); + dev_put(dev); + } +} + +static struct tq_struct stop_tq; + +static void stop_irq(int irq, void *unused, struct pt_regs *regs) +{ + stop_tq.routine = stop_task; + schedule_task(&stop_tq); } static int __init setup_stop_event(void) { - (void)request_irq(_EVENT_STOP, time_to_stop, 0, "stop", NULL); + (void)request_irq(_EVENT_STOP, stop_irq, 0, "stop", NULL); return 0; } diff --git a/xenolinux-2.4.22-sparse/arch/xeno/mm/init.c b/xenolinux-2.4.22-sparse/arch/xeno/mm/init.c index 633472b9d0..ef77f9629b 100644 --- a/xenolinux-2.4.22-sparse/arch/xeno/mm/init.c +++ b/xenolinux-2.4.22-sparse/arch/xeno/mm/init.c @@ -95,9 +95,8 @@ extern char _text, _etext, _edata, __bss_start, _end; extern char __init_begin, __init_end; static inline void set_pte_phys (unsigned long vaddr, - unsigned long phys, pgprot_t flags) + unsigned long phys, pgprot_t prot) { - pgprot_t prot; pgd_t *pgd; pmd_t *pmd; pte_t *pte; @@ -117,8 +116,6 @@ static inline void set_pte_phys (unsigned long vaddr, if (pte_val(*pte)) pte_ERROR(*pte); - pgprot_val(prot) = pgprot_val(PAGE_KERNEL) | pgprot_val(flags); - /* We queue directly, avoiding hidden phys->machine translation. */ queue_l1_entry_update(pte, phys | pgprot_val(prot)); @@ -129,8 +126,8 @@ static inline void set_pte_phys (unsigned long vaddr, __flush_tlb_one(vaddr); } -void __set_fixmap (enum fixed_addresses idx, unsigned long phys, - pgprot_t flags) +void __set_fixmap(enum fixed_addresses idx, unsigned long phys, + pgprot_t flags) { unsigned long address = __fix_to_virt(idx); @@ -138,7 +135,13 @@ void __set_fixmap (enum fixed_addresses idx, unsigned long phys, printk("Invalid __set_fixmap\n"); return; } - set_pte_phys(address, phys, flags); + set_pte_phys(address, phys, + __pgprot(pgprot_val(PAGE_KERNEL)|pgprot_val(flags))); +} + +void clear_fixmap(enum fixed_addresses idx) +{ + set_pte_phys(__fix_to_virt(idx), 0, __pgprot(0)); } static void __init fixrange_init (unsigned long start, @@ -229,9 +232,6 @@ void __init paging_init(void) vaddr = __fix_to_virt(__end_of_fixed_addresses - 1) & PMD_MASK; fixrange_init(vaddr, HYPERVISOR_VIRT_START, init_mm.pgd); - /* Cheesy: this can probably be moved to the blkdev driver. */ - set_fixmap(FIX_BLKRING_BASE, start_info.blk_ring); - /* Switch to the real shared_info page, and clear the dummy page. */ set_fixmap(FIX_SHARED_INFO, start_info.shared_info); HYPERVISOR_shared_info = (shared_info_t *)fix_to_virt(FIX_SHARED_INFO); diff --git a/xenolinux-2.4.22-sparse/include/asm-xeno/fixmap.h b/xenolinux-2.4.22-sparse/include/asm-xeno/fixmap.h index ec7083a359..2441b01d4e 100644 --- a/xenolinux-2.4.22-sparse/include/asm-xeno/fixmap.h +++ b/xenolinux-2.4.22-sparse/include/asm-xeno/fixmap.h @@ -72,6 +72,9 @@ extern void __set_fixmap (enum fixed_addresses idx, */ #define set_fixmap_nocache(idx, phys) \ __set_fixmap(idx, phys, PAGE_KERNEL_NOCACHE) + +extern void clear_fixmap(enum fixed_addresses idx); + /* * used by vmalloc.c. * -- 2.30.2